package com.luo.camunda.app.config;


import com.fasterxml.uuid.EthernetAddress;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.camunda.bpm.engine.impl.cfg.IdGenerator;
import org.camunda.bpm.engine.impl.persistence.StrongUuidGenerator;
import org.springframework.stereotype.Component;

/**
 * 该类实现的功能是在strongUuidGenerator基础上，去除类'-'
 */
@SuppressWarnings("ALL")
@Component
public class CustomUuidGenerator implements IdGenerator {
    
    protected static volatile TimeBasedGenerator timeBasedGenerator;
    
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    // private long twepoch = 1288834974657L;
    private long workerIdBits = 5L; // 节点ID长度
    private long datacenterIdBits = 5L; // 数据中心ID长度
    private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大支持机器节点数0~31，一共32个
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 最大支持数据中心节点数0~31，一共32个
    private long sequenceBits = 12L; // 序列号12位
    private long workerIdShift = sequenceBits; // 机器节点左移12位
    private long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心节点左移17位
    // private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //
    // 时间毫秒数左移22位
    private long sequenceMask = -1L ^ (-1L << sequenceBits); // 4095
    private long lastTimestamp = -1L;
    
    public CustomUuidGenerator() {
        ensureGeneratorInitialized(0,0);
    }
    
    protected void ensureGeneratorInitialized(long workerId, long datacenterId) {
        if (timeBasedGenerator == null) {
            synchronized (StrongUuidGenerator.class) {
                if (timeBasedGenerator == null) {
                    timeBasedGenerator = Generators.timeBasedGenerator(EthernetAddress.fromInterface());
    
                    if (workerId > maxWorkerId || workerId < 0) {
                        throw new IllegalArgumentException(
                                String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
                    }
                    if (datacenterId > maxDatacenterId || datacenterId < 0) {
                        throw new IllegalArgumentException(
                                String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
                    }
                    this.workerId = workerId;
                    this.datacenterId = datacenterId;
                    
                }
            }
        }
    }
    
    @Override
    public String getNextId() {
        //return timeBasedGenerator.generate().toString().replaceAll("-","");
        //自定义 nextId 保证 趋势 递增
        long timestamp = timeGen(); // 获取当前毫秒数
        // 如果服务器时间有问题(时钟后退) 报错。
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        // 如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            // sequence自增，因为sequence只有12bit，所以和sequenceMask相与一下，去掉高位
            sequence = (sequence + 1) & sequenceMask;
            // 判断是否溢出,也就是每毫秒内超过4095，当为4096时，与sequenceMask相与，sequence就等于0
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp); // 自旋等待到下一毫秒
            }
        } else {
            sequence = 0L; // 如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加
        }
        lastTimestamp = timestamp;
    
        StringBuilder idStr = new StringBuilder();
        String datePrefix = DateFormatUtils.format(timestamp, "yyyyMMddHHmmssSSS");
        idStr.append(datePrefix);
    
        Long suffix = (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
        int suffixLen = suffix.toString().length();
        if (suffixLen < 4) {
            idStr.append(StringUtils.repeat("0", 4 - suffixLen));
        }
        idStr.append(suffix);
    
        return idStr.toString();
    }
    
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    
    protected long timeGen() {
        return System.currentTimeMillis();
    }
    public long getWorkerId() {
        return workerId;
    }
    
    public void setWorkerId(long workerId) {
        this.workerId = workerId;
    }
    
    public long getDatacenterId() {
        return datacenterId;
    }
    
    public void setDatacenterId(long datacenterId) {
        this.datacenterId = datacenterId;
    }
    
    //自测
    public static void main(String[] args){
        long now = System.currentTimeMillis();
        CustomUuidGenerator uuid = new CustomUuidGenerator();
        CustomUuidGenerator uuid2 = new CustomUuidGenerator();
        uuid2.setWorkerId(1);
        uuid2.setDatacenterId(0);
        
        for(int i = 0;i < 1000;i ++){
            String nextId = uuid.getNextId();
            
            String next2 = uuid2.getNextId();
            System.out.println(i+"nextId1:"+nextId);
            System.out.println(i+"nextId2:"+next2);
            
        }
        
        System.out.println("cost:"+(System.currentTimeMillis()- now));
    }
    
    
}

